Taeseong Blog

Figma에서 TypeScript까지, 디자인 토큰 자동화 파이프라인 구축하기

2026-01-12

디자인 토큰자동화

1. 왜 디자인 토큰 자동화가 필요할까?

image.png

디자인이 바뀔 때마다 이런 경험을 해본 적이 있을 겁니다.

디자이너는 Figma에서 색상을 바꿨는데 개발자는 예전에 복사해 둔 값을 그대로 쓰고 있고 결과적으로 화면은 디자인과 어긋나 있습니다

이 과정에서 자연스럽게 다음과 같은 문제가 생깁니다.

  1. 색상이나 폰트 값을 수동으로 복사/붙여넣기 하면서 생기는 오타
  2. 디자이너와 개발자가 서로 다른 기준의 값을 보고 작업
  3. 변경된 디자인을 일일이 비교하고 검수해야 하는 비용 증가

문제의 원인은 디자인에 대한 결정이 한 곳에 모여 있지 않기 때문입니다.

이를 방지하고자 디자이너가 Figma를 통해 디자인 시스템에 정의한 색상, 폰트, 간격 같은 요소를 코드로 자동 변환하는 시스템을 구축해보려 합니다.

자동화 프로세스

디자인 토큰 자동화 프로세스는 다음 과정을 거칩니다.

1. 토큰 관리: Tokens Studio와 GitHub를 사용하여 토큰을 중앙 집중화하고 동기화합니다. 디자인 토큰은 Primary 색상은 얼마인지, 기본 텍스트 색상은 무엇인지와 같은 값들이 컴포넌트 곳곳에 흩어지지 않고 하나의 JSON에만 존재하도록 합니다. 즉, 개발자 입장에서는 디자인용 상수 파일이라고 보면 됩니다.

2. 토큰 변환: 디자인 토큰 JSON은 그대로는 사용할 수 없습니다. 그래서 Style Dictionary를 사용하여 디자인 토큰을 프론트엔드 코드에 적합한 형식으로 변환합니다.

3. 타입 안전: “이 프로젝트에서 허용되는 디자인 토큰 구조”를 TypeScript 타입으로 고정해 줍니다. 즉, 디자인 토큰을 타입으로 생성하여 토큰 구조를 정의해 오타를 방지하고 자동 완성 기능을 사용하여 DX를 높입니다,

4. 통합: vanilla-extract(또는 그 외의 CSS 라이브러리)를 사용하여 디자인 토큰을 CSS 변수와 통합합니다.

용어 정리 (디자인 토큰, 자동화 파이프라인, Contract)

2. 디자인 토큰 정의 및 Github 연동

Figma에서는 디자인 토큰을 정의할 수 있도록 공식 플러그인을 제공하며, 디자이너는 Tokens Studio를 사용해 컬러, 타이포그래피, 스페이싱 등의 디자인 토큰을 관리할 수 있습니다.

Tokens Studio에서 토큰을 매번 수동으로 Export하는 방식 대신, GitHub와 연동하면 디자인 토큰이 변경될 때마다 Push만으로 토큰을 동기화할 수 있습니다. 이를 통해 토큰 변경 사항을 보다 효율적으로 개발 환경과 연결할 수 있습니다.

GitHub 연동을 위해서는 Tokens Studio 설정에서 필요한 정보들을 입력하면 되는데요, 해당 연동 설정은 Figma의 디자인 모드에서만 가능합니다. 또한 토큰을 수정하고 Push하는 작업 역시 디자인 모드에서 수행되므로, 디자인 토큰의 관리와 배포는 디자이너의 역할입니다.

보통은 토큰 전용 GitHub 레포지토리에서 토큰을 가져오는 방식을 사용합니다. 디자인 변경 이력과 코드 변경 이력이 섞이지 않고 여러 앱에서 같은 디자인 토큰을 재사용할 수 있기 때문입니다.

3. Style Dictionary로 가공하기

이전까지의 과정을 거치셨다면, 디자인 토큰 레포에 token.json이 정상적으로 들어왔을 탠데요. 이 토큰을 사용할 프로젝트에 해당 토큰을 가져오는 과정을 거쳐야 합니다.

다음과 같이 스크립트를 작성합니다.

#!/usr/bin/env bash
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
DEST_TOKENS_PATH="$ROOT_DIR/tokens/design-tokens.json"
REMOTE_URL="${LEMON_TOKENS_REMOTE_URL:-"https://github.com/{디자인토큰 레포 주소}.git"}"
REMOTE_BRANCH="${LEMON_TOKENS_REMOTE_BRANCH:-main}"
REMOTE_PATH="${LEMON_TOKENS_REMOTE_PATH:-tokens.json}"

mkdir -p "$(dirname "$DEST_TOKENS_PATH")"

TMP_DIR="$(mktemp -d)"
cleanup() {
  rm -rf "$TMP_DIR"
}
trap cleanup EXIT

git -C "$TMP_DIR" init -q
git -C "$TMP_DIR" fetch -q --depth=1 "$REMOTE_URL" "$REMOTE_BRANCH"
git -C "$TMP_DIR" show "FETCH_HEAD:$REMOTE_PATH" > "$DEST_TOKENS_PATH"

echo "Updated tokens from remote: $DEST_TOKENS_PATH"

저는 package.json에 tokens:pull이라는 명령어를 작성해 작성한 스크립트를 실행하도록 했습니다.

  "scripts": {
    "tokens:pull": "bash scripts/update-tokens.sh && pnpm tokens:build",
    "tokens:build": "node scripts/build-tokens.mjs"
  },

tokens:pull을 보시면 pnpm tokens:build이라는 명령어도 함께 실행하는 것을 볼 수 있습니다. tokens:build는 디자인 토큰을 Style Dictionary를 통해 프론트엔드 코드에서 사용할 수 있도록 변환하는 스크립트를 실행시키는 명령어입니다.

Style Dictionary가 필요한 이유

Tokens Studio를 통해 추출한 디자인 토큰 JSON을 보면 다음과 같은 구조를 가집니다.

{
  "01 Palette": {
    "Neutral": {
      "50": { "value": "#f8f8f8", "type": "color" }
    }
  },
  "02 Semantic": {
    "Semantic": {
      "Background": {
        "Default": {
          "value": "{Neutral.50}",
          "type": "color"
        }
      }
    }
  }
}

이 구조는 디자인 툴에서는 사용할 수 있지만, 빌드나 코드에서 직접 사용할 수 없습니다. Style Dictionary는 이 JSON을 프론트엔드 코드에서 활용할 수 있도록 변환하는 역할을 합니다.

// Tokens Studio 형식의 디자인 토큰을 Style Dictionary로 변환해, tokens/generated/tokens.json 파일로 가공
// 전체 스크립트를 포함하면 글이 너무 길어져, 일부 코드만 포함합니다.
// AI를 활용하면 쉽게 작성할 수 있으니 AI를 활용하는 것을 추천합니다.
const sd = new StyleDictionary({
  tokens,
  preprocessors: ['tokens-studio'],
  platforms: {
    json: {
      transformGroup: 'tokens-studio',
      buildPath: 'tokens/generated/',
      files: [
        {
          destination: 'tokens.json',
          format: 'json/nested',
        },
      ],
    },
  },
});

프론트엔드 코드에서 사용할 수 있도록 가공된 JSON의 예시는 다음과 같습니다.

{
  "Neutral": {
    "50": "#f8f8f8",
    "100": "#f0f0f0",
    "200": "#e6e6e6",
    "300": "#d5d5d5",
    "400": "#a8a8a8",
    "500": "#909090",
    "600": "#686868",
    "700": "#555555",
    "800": "#373737",
    "900": "#171717"
  },
  "Yellow": {
    "50": "#fffde7",
    "100": "#fffac3",
    "200": "#fef69a",
    "300": "#fdf170",
    "400": "#fff152",
    "500": "#fceb2d",
    "600": "#fedb2a",
    "700": "#fdc220",
    "800": "#fcaa16",
    "900": "#f98005"
  },
  "Blue": {
    "50": "#e4f2ff",
    "100": "#bdddff",
    "200": "#93c8ff",
    "300": "#68b2ff",
    "400": "#4ba1ff",
    "500": "#3991ff"
    // ... 생략
  }
  // ... 생략
}

4. Typescript로 한번 더 가공하기

Style Dictionary 변환 결과로 생성되는 파일은 다음과 같습니다.

  • tokens/generated/tokens.json

여기서 Typescript로 한번 더 가공하는 과정을 거칠건데요. 이유는 다음과 같습니다. (글이 너무 길어져 코드는 생략합니다. AI를 통해 변환하는 코드를 작성하는 것을 추천합니다.)

  • 토큰 키 오타를 빌드 시점에 잡아준다
  • 자동완성을 통해 개발 경험을 좋게 만든다
  • 토큰 구조가 바뀌면 영향 범위를 즉시 알 수 있다

image.png

다음 과정을 통해 TypeScript 계약(contract) 파일을 자동으로 생성합니다. 생성 방식은 다음과 같습니다.

  1. tokens/generated/tokens.json을 읽는다.
  2. 값은 모두 null로 바꾼다.
  3. 구조만 남긴 객체를 as const로 export 한다.
// tokens/generated/contract.ts
export const tokensContract = {
  Neutral: {
    50: null,
    100: null,
  },
} as const;

이 파일은 말 그대로 “토큰 구조에 대한 타입 정의서”입니다.

5. 사용 예시

저는 프로젝트에서 vanilla-extract 기반 CSS 변수를 사용합니다. 위에서 만들어진 contract.ts 파일을 가지고 createThemeContract(tokensContract)로 구조를 생성하고 createTheme로 실제 값과 매핑합니다.

생성된 themeClass를 에 적용하면, 모든 컴포넌트에서 같은 토큰을 사용할 수 있습니다.

<body className={themeClass}>{children}</body>

6. 마치며

디자인 토큰 파이프라인을 자동화함으로써 토큰 변경 사항을 diff로 명확히 추적이 가능하고, 특정 시점의 토큰을 기준으로 안정적인 릴리즈가 가능합니다. 무엇보다, 디자이너와 개발자 사이의 커뮤니케이션 비용 감소가 가장 명확한 장점입니다.

또, 다음과 같이 디자인 토큰과 타입스크립트를 혼합하여 사용하면

backgroundColor: vars.Semantic.Background.Default;

vars 객체는 contract를 기반으로 만들어졌기 때문에, 존재하는 토큰 키는 자동완성으로 바로 확인할 수 있고 존재하지 않는 키를 쓰면 즉시 타입 에러가 발생합니다. 즉, “이 토큰이 맞는지 아닌지”를 런타임이 아니라 코드 작성 시점에 알 수 있습니다.